﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
using Devmil.MVVM;

namespace Devmil.MVVM.Notification
{
    public static class NotifyPropertyChangedHandler
    {
        private static bool _PausePropertyChangedEvents = false;

        public static void PausePropertyChangedEvents()
        {
            _PausePropertyChangedEvents = true;
        }

        public static void ResumePropertyChangedEvents()
        {
            lock (_PausedPropertyChangedEvents)
            {
                _PausePropertyChangedEvents = false;
                foreach (var kvp in _PausedPropertyChangedEvents)
                {
                    WeakReference<INotifyPropertyChanged> obj = kvp.Key;
                    if (!obj.IsAlive)
                        continue;
                    foreach (var propertyName in kvp.Value)
                    {
                        OnNotifyPropertyChanged(obj.Target, propertyName);
                        obj.Target.PropagateDependentChanges(propertyName);
                    }
                }
                _PausedPropertyChangedEvents.Clear();
            }
        }

        private static void RegisterPropertyChanged(INotifyPropertyChanged obj, string propertyName)
        {
            if (!_PausePropertyChangedEvents)
                return;
            lock (_PausedPropertyChangedEvents)
            {
                List<string> propertyNames;
                if (!_PausedPropertyChangedEvents.TryGetValue(new WeakReference<INotifyPropertyChanged>(obj), out propertyNames))
                    _PausedPropertyChangedEvents.Add(new WeakReference<INotifyPropertyChanged>(obj), propertyNames = new List<string>());
                if (!propertyNames.Contains(propertyName))
                    propertyNames.Add(propertyName);
            }
        }

        private static Dictionary<WeakReference<INotifyPropertyChanged>, List<string>> _PausedPropertyChangedEvents = new Dictionary<WeakReference<INotifyPropertyChanged>, List<string>>();

        public static void OnNotifyPropertyChanged(INotifyPropertyChanged @object, string propertyName)
        {
            if (_PausePropertyChangedEvents)
            {
                RegisterPropertyChanged(@object, propertyName);
            }
            else
            {
                if (@object is IFireNotifyPropertyChanged)
                {
                    ((IFireNotifyPropertyChanged)@object).FireNotifyPropertyChanged(propertyName);
                }
                else
                {
#if WP7
                    FieldInfo propertyChangedField = @object.GetType().GetField("PropertyChanged", BindingFlags.Instance | BindingFlags.NonPublic);
#else
                    FieldInfo propertyChangedField = @object.GetType().GetTypeInfo().DeclaredFields.FirstOrDefault(p => p.Name == "PropertyChanged");
#endif
                    Delegate propertyChangedDelegate = (Delegate)propertyChangedField.GetValue(@object);
                    if (propertyChangedDelegate != null)
                        propertyChangedDelegate.DynamicInvoke(@object, new PropertyChangedEventArgs(propertyName));
                }
            }
        }

        public static string GetMemberName<T, R>(Expression<Func<T, R>> expression)
        {
            if (!(expression.Body is MemberExpression))
                throw new Exception("Only Member Expressions are supported!");
            return ((MemberExpression)expression.Body).Member.Name;
        }
    }
}
